package com.uzhie.media;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.uzhie.media.bean.MediaMsgBean;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.ReferenceCountUtil;
import org.apache.log4j.Logger;

import java.nio.charset.Charset;
import java.util.List;

/**
 * Created by TangFei on 2018/8/7.
 * 流媒体协议解码
 */
public class MediaByteDecoder extends ByteToMessageDecoder {

    private Logger logger = Logger.getLogger(this.getClass());

    // 分隔符
    private byte[] delimiter;
    // 消息最大长度
    private int maxLength;

    public MediaByteDecoder() {
    }

    public MediaByteDecoder(byte[] delimiter) {
        this.delimiter = delimiter;
        this.maxLength = 1024;
    }

    public MediaByteDecoder(byte[] delimiter, int maxLength) {
        this.delimiter = delimiter;
        this.maxLength = maxLength;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        int readerIndex = byteBuf.readerIndex();
        int readableBytes = byteBuf.readableBytes();
        logger.info("收到的字节：" + ByteBufUtil.hexDump(byteBuf) + " readableBytes 长度：" + readableBytes);
        if (readableBytes < 8) {
            logger.error("报文长度过短！等待后面的包到达");
            return;
        } else if (readableBytes < maxLength) {
            logger.info("当前报文：" + ByteBufUtil.hexDump(byteBuf));
            ByteBuf byteBuf1 = Unpooled.copiedBuffer(delimiter);
            int i = ByteBufUtil.indexOf(byteBuf1, byteBuf);
            ReferenceCountUtil.release(byteBuf1);
            if (i > -1) {
                byteBuf.skipBytes(i);
                logger.info("读取到了标识符 " + ByteBufUtil.hexDump(delimiter) + " 下标 " + i);
                byte[] receiveDelimiterBytes = new byte[4];
                byteBuf.readBytes(receiveDelimiterBytes, 0, 4);
                byte[] msgIDBytes = new byte[2];
                byteBuf.readBytes(msgIDBytes, 0, 2);
                byte[] bodyLengthBytes = new byte[2];
                byteBuf.readBytes(bodyLengthBytes, 0, 2);
                int bodyLength = parseByteArrayToInt(bodyLengthBytes);
                readableBytes = byteBuf.readableBytes();
                if (readableBytes >= bodyLength) {
                    logger.info("正常包或者出现了粘包");
                    byte[] bodyBytes = new byte[bodyLength];
                    byteBuf.readBytes(bodyBytes, 0, bodyLength);
                    byteBuf.discardReadBytes();
                    MediaMsgBean mediaMsgBean = parseByteArrayToObj(msgIDBytes, bodyLength, bodyBytes);
                    logger.info("收到消息ID：" + mediaMsgBean.getHexMsgID() + " 消息体：" + mediaMsgBean.getMsgBody() + "消息体字节：" + ByteBufUtil.hexDump(bodyBytes));
                    list.add(mediaMsgBean);
                } else {
                    logger.info("出现了拆包，等待后面的字节到达");
                    byteBuf.readerIndex(readerIndex);
                    return;
                }
            } else {
                logger.info("未找到标识符，等待后面的字节到达");
                byteBuf.readerIndex(readerIndex);
                return;
            }
        } else {
            logger.error("接收报文过长！报文允许的最大长度为：" + maxLength + " 清空报文，等待后面的包到达");
//            ReferenceCountUtil.release(byteBuf);
            byteBuf.clear();
            return;
        }
    }

    private int parseByteArrayToInt(byte[] bytes) throws RuntimeException {
        int result = 0;
        for (int i = 0; i < bytes.length; i++) {
            result += (bytes[i] & 0xFF) << ((bytes.length - 1 - i) * 8);
        }
        return result;
    }

    private MediaMsgBean parseByteArrayToObj(byte[] msgIDBytes, int bodyLength, byte[] bodyBytes) {
        MediaMsgBean mediaMsgBean = new MediaMsgBean();
        int msgID = parseByteArrayToInt(msgIDBytes);
        mediaMsgBean.setMsgID(msgID);
        mediaMsgBean.setBodyLength(bodyLength);
        JSONObject bodyObject = JSON.parseObject(new String(bodyBytes, Charset.forName("GBK")));
        mediaMsgBean.setMsgBody(bodyObject);
        return mediaMsgBean;
    }

}
